; ** BRACES.M

; ** This macro examines the character at the cursor. If it is any of the
; ** 4 characters (){} it tries to find the matching character. For C programs
; ** it correctly handles comments, and string and character constants.

(macro braces
	(
		(int	dir		; ** 1 = forward, 0 = backward
				lev		; ** search level
				cod		; ** search return code
		)
		(string	target	; ** search string
				skip	; ** used to skip strings and comments
		)
		; ** determine search argument and direction
		(= lev 1)
		(if (== "(" (= target (read 1) ) )
			(
				(= target "[))((\"']|{/\\*}")
				(= dir 1)
			)
			;else
			(if (== target "{")
				(
					(= target "[\\}\\{\"']|{/\\*}")
					(= dir 1)
				)
				;else
				(if (== target ")")
					(
						(= target "[(())\"']|{\\*/}")
						(= dir 0)
					)
					;else
					(if (== target "}")
						(
							(= target "[\\{\\}\"']|{\\*/}")
							(= dir 0)
						)
						;else
						(= lev -2)
					)
				)
			)
		)
		; ** search loop
		(message "Searching...")
		(while (> lev 0)
			(
				; ** next search
				(if (> dir 0)
					(
						(next_char)
						(= cod (search_fwd target) )
					)
					;else
					(if (_can_rev 1)
						(= cod (search_back target) )
						;else
						(= cod 0)
					)
				)
				; ** handle search results
				(if (> cod 0)
					(
						(= cod (index target (read 1) ) )
						(if (< cod 4) 
							(= lev (- lev 1) )	; ** opposite pair
							;else
							(if (< cod 6)
								(= lev (+ lev 1) )	; ** same guy
								;else		; ** must bypass something
								(
									; ** setup for bypass
									(if (== cod 6)
										(= skip "[~\\\\]\\c\"" )
										;else
										(if (== cod 7)
											(= skip "[~\\\\]\\c'" )
											;else
											(if (> dir 0)
												(
													(= skip "\\*/")
													(next_char)
													(next_char)
												)
												;else
												(= skip "/\\*")
											)
										)
									)
									; ** try and bypass
									(if (> dir 0)
										(= cod (search_fwd skip) )
										;else
										(if (_can_rev 2)
											(= cod (search_back skip) )
											;else
											(= cod 0)
										)
									)
								)
							)
						)
					)
				)
				(if (< cod 1)
					(= lev -1)				; ** search failed
				)
			)
		)
		(refresh)							; ** show where we are
		(if (== lev 0)
			(message "Match found.")
			;else
			(
				(if (== lev -1)
					(message "No Match.")
					;else
					(message "Invalid: must be {, }, ( or ).")
				)
				(beep)						; ** signal error
			)
		)
	)
)

; ** this macro is used to determine whether or not a reverse search is
; ** possible. it attempts to prev_char <parameter> number of times.
; ** it returns 0 if the reversing was not successful (top of buffer).

(macro _can_rev
	(
		(int	cnt		; ** reverse count
				lin		; ** current line number
				col		; ** current column number
		)
		(get_parm 0 cnt)					; ** number of prev_char(s)
		(while (> cnt 0)
			(
				(inq_position lin col)				; ** where am i?
				(prev_char)							; ** try to back up
				(= cnt (- cnt 1) )					; ** decrement count
				(if (&& (== lin 1) (== col 1) )
					(returns 0)	  					; ** top of buffer
					;else
					(returns 1)						; ** elsewhere
				)
			)
		)
	)
)
